Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | export const dynamic = "force-dynamic"; /** * Support Article Detail API * GET /api/support/articles/[slug] - Get article by slug */ import { NextRequest, NextResponse } from 'next/server'; import { prisma } from "@/lib/prisma"; import { incrementArticleViews } from "@/lib/support/chatbot-utils"; import { logger } from "@/lib/logging"; import { withErrorHandling, successResponse, ApiError, ApiSuccessResponse } from "@/lib/api"; import { RouteContext } from "@/lib/api/middleware"; import { SupportArticle } from "@prisma/client"; /** * GET /api/support/articles/[slug] * Get article by slug (public) */ async function handleGet( _request: NextRequest, context?: RouteContext ): Promise<NextResponse<ApiSuccessResponse<SupportArticle>>> { if (!context?.params) { throw ApiError.validation("Article slug is required"); } const { slug } = await context.params; // Find the article const article = await prisma.supportArticle.findUnique({ where: { slug }}); // Only return published articles for public API if (!article || !article.isPublished) { throw ApiError.notFound("Article", slug); } // Increment view count (fire and forget) incrementArticleViews(article.id).catch((err) => { logger.error("Failed to increment article views", err instanceof Error ? err : new Error(String(err)), { category: "API" }); }); return successResponse(article); } export const GET = withErrorHandling(handleGet); |